#coding=utf8

from mytoken import *

class Scanner(object):

    def __init__(self, file):
        self.windowSize = len(" System.out.println ")
        self.file = file
        self.text = " " + self.file.read() + " " * self.windowSize
        self.RemoveExtraSpaces()
        self.Reset()

    def Reset(self):
        self.tokens = list()
        self.caretPosition = 0

    def GetNextToken(self, caretShift = True):
        """ Считывает следующий токен и возвращает его тип и строковое
        представление """
        if self.caretPosition in range(0, len(self.text) -
        self.windowSize + 1):
            window = self.text[self.caretPosition \
                : self.caretPosition + self.windowSize]
            token = self.FindToken(window)
            if token.Type() != T.NONE:
                #print window[1:-1]
                self.tokens.append(token)
                if caretShift:
                    self.caretPosition += len(token.Value())
                return token
            if caretShift:
                self.caretPosition += 1
            return Token(T.NONE)
        else:
            return Token(T.EOF)

    def GetNthPastToken(self, n):
        """ Возвращает токен, считанный n токенов назад. N=0 для последнего
        считанного токена, N=-1 для следующего токена (каретка при этом не
        сдвигается """
        if n >= 0:
            return self.tokens[-n - 1]
        else:
            return self.GetNextToken(False)

    def GetAllTokens(self):
        """ Главный метод сканера, возвращает список экземпляров Token """
        token = self.GetNextToken()
        while token != T.EOF:
            self.tokens.append(token)
            token = self.GetNextToken()
        return self.tokens

    def RemoveExtraSpaces(self):
        """ Заменяет последовательности пробелов длиной больше 1 на один
        пробел """
        prevCh = ""
        newText = ""
        for ch in self.text:
            if ch != '\n' and not (self.IsWhitespace(ch)
            and self.IsWhitespace(prevCh)):
                newText += ch
                prevCh = ch
        self.text = newText

    def IsWhitespace(self, ch):
        """ Определяет, является ли символ пробелом или переносом строки """
        return ch.isspace() or ch == "\n"

    def IsAllowedNearId(self, ch):
        """ Определяет, может ли символ соседствовать с идентификатором или
        буквенным оператором. """
        return (ch.isspace() or ch == "(" or ch == ")" or ch == "["
            or ch == "]" or ch == "{" or ch == "}" or ch == "<" or ch == ";"
            or ch == "&" or ch == "." or ch == "=" or ch == "+" or ch == "-"
            or ch == "*" or ch == "!")

    def IsLetter(self, ch):
        """ Буква ли """
        chL = ch.lower()
        return (chL == "a" or chL == "b" or chL == "c" or chL == "d"
            or chL == "e" or chL == "f" or chL == "g" or chL == "h"
            or chL == "i" or chL == "j" or chL == "k" or chL == "l"
            or chL == "m" or chL == "n" or chL == "o" or chL == "p"
            or chL == "q" or chL == "r" or chL == "s" or chL == "t"
            or chL == "u" or chL == "v" or chL == "w" or chL == "x"
            or chL == "y" or chL == "z")

    def IsStartingNewLine(self, ch):
        """ Определяет, предшествует ли символ новой команде """
        return ch == "{" or ch == "}" or ch == ";" or ch == "(" or self.IsWhitespace(ch)

    def FindToken(self, window):
        """ Возвращает найденный в окошке токен.
        Рабочая область окошка начинается со 2 символа. """

        if self.IsWhitespace(window[1]):
        # ' '
            return Token(T.WHITESPACE, window[1])

        elif ((self.IsStartingNewLine(window[0]) or self.IsWhitespace(window[0])) and window[1:6] == "class"
        and self.IsAllowedNearId(window[6])):
        # <'{' | '}' | ';' | whitespace>class<ANI>
            return Token(T.CLASS, window[1:6])

        elif ((self.IsStartingNewLine(window[0]) or self.IsWhitespace(window[0])) and window[1:7] == "public"
        and self.IsAllowedNearId(window[7])):
        # <'{' | '}' | ';' | whitespace>public<ANI>
            return Token(T.PUBLIC, window[1:7])

        elif ((self.IsStartingNewLine(window[0]) or self.IsWhitespace(window[0])) and window[1:7] == "static"
        and self.IsAllowedNearId(window[7])):
        # <'{' | '}' | ';' | whitespace>static<ANI>
            return Token(T.STATIC, window[1:7])

        elif ((self.IsStartingNewLine(window[0]) or self.IsWhitespace(window[0])) and window[1:5] == "void"
              and self.IsAllowedNearId(window[5])):
        # <'{' | '}' | ';' | whitespace>void<whitespace>
            return Token(T.VOID, window[1:5])

        elif ((self.IsStartingNewLine(window[0]) or self.IsWhitespace(window[0])) and window[1:8] == "extends"
              and self.IsAllowedNearId(window[8])):
        # <'{' | '}' | ';' | whitespace>extends<whitespace>
            return Token(T.EXTENDS, window[1:8])

        elif (self.IsWhitespace(window[0]) and window[1:5] == "main"
        and (window[5] == "(" or self.IsWhitespace(window[5]))):
        # <whitespace>main<whitespace | '('>
            return Token(T.MAIN, window[1:5])

        elif window[1] == "(":
        # '('
            return Token(T.LEFT_BRACKET, window[1])

        elif window[1] == ")":
        # ')'
            return Token(T.RIGHT_BRACKET, window[1])

        elif window[1] == "[":
        # '['
            return Token(T.LEFT_QUAD_BRACKET, window[1])

        elif window[1] == "]":
        # ']'
            return Token(T.RIGHT_QUAD_BRACKET, window[1])

        elif window[1] == "{":
        # '{'
            return Token(T.LEFT_FIGURE_BRACKET, window[1])

        elif window[1] == "}":
        # '}'
            return Token(T.RIGHT_FIGURE_BRACKET, window[1])

        elif window[1] == "<":
        # '{'
            return Token(T.LESS, window[1])

        elif window[1] == ".":
        # '.'
            return Token(T.DOT, window[1])

        elif window[1] == ";":
        # ';'
            return Token(T.SEMICOLON, window[1])

        elif window[1] == "=":
        # '='
            return Token(T.EQUAL, window[1])

        elif window[1:3] == "&&":
        # '&&'
            return Token(T.AND, window[1:3])

        elif (self.IsStartingNewLine(window[0]) and window[1:7] == "String"
              and self.IsAllowedNearId(window[7])):
        # <'{' | '}' | ';' | '(' whitespace>String<ANI>
            return Token(T.STRING, window[1:7])

        elif (self.IsStartingNewLine(window[0]) and window[1:4] == "int"
              and self.IsAllowedNearId(window[4])):
        # <'{' | '}' | ';' | '(' | whitespace>int<ANI>
            return Token(T.INT, window[1:4])

        elif (self.IsStartingNewLine(window[0]) and window[1:8] == "boolean"
              and self.IsAllowedNearId(window[8])):
        # <'{' | '}' | ';' | '(' | whitespace>boolean<ANI>
            return Token(T.BOOLEAN, window[1:8])

        elif (self.IsStartingNewLine(window[0]) and window[1:3] == "if"
              and self.IsAllowedNearId(window[3])):
        # <'{' | '}' | ';' | '(' | whitespace>if<ANI>
            return Token(T.IF, window[1:3])

        elif (self.IsStartingNewLine(window[0]) and window[1:19] == "System.out.println"
              and (window[19] == "(" or self.IsWhitespace(window[19]))):
        # <'{' | '}' | ';' | '(' | whitespace>System.out.println<ANI>
            return Token(T.PRINT, window[1:19])

        elif (window[0] == '.' and window[1:7] == "length"
              and self.IsAllowedNearId(window[7])):
        # .length<ANI>
            return Token(T.LENGTH, window[1:7])

        elif (self.IsAllowedNearId(window[0]) and window[1:5] == "true"
              and self.IsAllowedNearId(window[5])):
        # <ANI>true<ANI>
            return Token(T.TRUE, window[1:5])

        elif (self.IsAllowedNearId(window[0]) and window[1:6] == "true"
              and self.IsAllowedNearId(window[6])):
        # <ANI>false<ANI>
            return Token(T.FALSE, window[1:6])

        elif self.IsAllowedNearId(window[0]):
        # <ANI><identifier><?>
            if self.IsLetter(window[1]):
                i = 1
                while self.IsLetter(window[i]) or unicode(window[i]).isnumeric():
                    i += 1
                id = window[1:i]
                #print id
                if i > 1 and self.IsAllowedNearId(window[i]):
                    return Token(T.IDENTIFIER, id)
                else:
                    return Token(T.NONE)

        """elif self.IsAllowedNearId(window[0]):
        # <ANI><integer value><?>
            if unicode(window[1]).isnumeric():
                i = 1
                while unicode(window[i]).isnumeric():
                    i += 1
                value = window[1:i]
                if i > 1 and self.IsAllowedNearId(window[i]):
                    return Token(T.INTEGER_VALUE, value)
                else:
                    return Token(T.NONE)"""

        return Token(T.NONE)